﻿using System.Text;
using FunctionalCLR.Core;
using FunctionalCLR.Core.Function;
using FunctionalCLR.Core.Function.Arithmetics;
using FunctionalCLR.Core.Function.Logic;
using FunctionalCLR.Core.Functions.Maths;
using FunctionalCLR.Shell.Function;

namespace Zove.Runtime {
    public class UserDefinedFunctionConverter : IVisitor {

        private ReplacingMode replacingMode;

        public string ClassName {
            get;
            set;
        }

        public UserDefinedFunctionConverter()
            : this(string.Empty, ReplacingMode.CompleteMatch) {
        }

        public UserDefinedFunctionConverter(string className)
            : this(className, ReplacingMode.CompleteMatch) {
        }

        public UserDefinedFunctionConverter(
            string className, ReplacingMode mode) {
            this.ClassName = className;
            this.replacingMode = mode;
        }

        #region IVisitor Members

        public object Visit(IdentifierNode node) {
            return node.Value;
        }

        public object Visit(NumberNode node) {
            return node.Value;
        }

        public object Visit(StringNode node) {
            return node.Value;
        }

        public object Visit(SubNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} - {1})", lr, rr);
        }

        public object Visit(AddNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} + {1})", lr, rr);
        }

        public object Visit(MulNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} * {1})", lr, rr);
        }

        public object Visit(DivNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} / {1})", lr, rr);
        }

        public object Visit(PowerNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} ^ {1})", lr, rr);
        }

        public object Visit(LogNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} ' {1})", lr, rr);
        }

        public object Visit(LargerThanNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} > {1})", lr, rr);
        }

        public object Visit(LargerEqualToNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} >= {1})", lr, rr);
        }

        public object Visit(LessThanNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} < {1})", lr, rr);
        }

        public object Visit(LessEqualToNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} <= {1})", lr, rr);
        }

        public object Visit(EqualNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} == {1})", lr, rr);
        }

        public object Visit(NotEqualNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} != {1})", lr, rr);
        }

        public object Visit(NotNode node) {
            object lr = node.Operand.Accept(this);

            return string.Format("(!{0})", lr);
        }

        public object Visit(FuncNode node) {
            if (node is UserDefinedRuleNode) {
                UserDefinedRuleNode udf
                    = node as UserDefinedRuleNode;
                string expr = udf.FuncBody;

                for (int index = udf.Arguments.Length - 1; 
                    index >= 0; index--) {
                    object value 
                        = node.Parameters[index].Accept(this);
                    expr = Utils.ReplaceString(
                        udf.Arguments[index],
                        value.ToString(), expr, 
                        ReplacingMode.StartingMatch
                        );
                }

                return expr;
            }
            else if (node is UserDefinedFunctionNode) {
                UserDefinedFunctionNode udf
                = node as UserDefinedFunctionNode;
                string expr = udf.FuncBody;

                for (int index = udf.Arguments.Length - 1;
                    index >= 0; index--) {
                    object value = node.Parameters[index].Accept(this);
                    expr = Utils.ReplaceString(
                        udf.Arguments[index],
                        value.ToString(), expr,
                        ReplacingMode.CompleteMatch
                        );
                }

                return expr;
            }

            StringBuilder buff = new StringBuilder();

            buff.Append(node.Value).Append("(");
            for (int index = node.Parameters.Count - 1; index >= 0; index--) {
                object value = node.Parameters[index].Accept(this);
                buff.Append(value.ToString());
                if (index > 0) {
                    buff.Append(",");
                }
            }
            buff.Append(")");

            return buff.ToString();
        }

        #endregion

        public string Convert(Node root) {
            return (string)root.Accept(this);
        }

        #region IVisitor Members


        public object Visit(AndNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} && {1})", lr, rr);
        }

        public object Visit(OrNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} || {1})", lr, rr);
        }

        #endregion

        #region IVisitor Members


        public object Visit(RemainderNode node) {
            object lr = node.Left.Accept(this);
            object rr = node.Right.Accept(this);

            return string.Format("({0} % {1})", lr, rr);
        }

        #endregion
    }
}
